package ongd.negocio.gestion;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import ongd.negocio.sesion.ControladorSesion;
import ongd.servicios.persistencia.ControladorPersistencia;
import ongd.servicios.persistencia.exceptions.IllegalOrphanException;
import ongd.servicios.persistencia.exceptions.NonexistentEntityException;
import ongd.servicios.persistencia.exceptions.PreexistingEntityException;
import org.primefaces.model.TreeNode;

/**
 * Clase que realiza todas las operaciones referentes a la entidad indices
 * de la base de datos.
 *
 * @author <a href="mailto:egi0002@alu.ubu.es">Elena García Iglesias</a>
 * @author <a href="mailto:cms0021@alu.ubu.es">Cristina Martínez Sanz</a>
 * @version 1.0 20120124
 */
public class ControladorIndice {

    /**
     * Objeto del tipo controlador persistencia que nos servirá para crear 
     * las instancias de las diferentes entidades.
     */
    private ControladorPersistencia contP;
    /**
     * Objeto del tipo controlador sesion que nos servirá para sabre 
     * en que sesion estamos.
     */
    private ControladorSesion contSesion;
    /**
     * Objeto de tipo HashMap que va a contener el indice asociado con una
     * página .html
     */
    private HashMap<Indice, String> hsmPaginas;

    /**
     * Constructor de la clase.
     * @param contP controlador Persistencia
     * @param contSesion sesión en la que estamos
     */
    public ControladorIndice(ControladorPersistencia contP, ControladorSesion contSesion) {
        this.contP = contP;
        this.contSesion = contSesion;
        hsmPaginas = new HashMap<Indice, String>();
    }

    /**
     * Método que crea un objeto de tipo indice en la base de datos.
     * @param nodo Tree que contiene los nodos
     * @param padre Indice padre
     * @throws PreexistingEntityException 
     * @throws Exception 
     */
    public void crearIndice(TreeNode nodo, Indice padre) throws PreexistingEntityException, Exception {
        if (nodo != null) {
            Indice indice = new Indice();
            int idIndice = 1;
            //Obtengo el último id que existe en la base de datos
            List<? extends Entidad> listaIndices = contP.obtenerEntidades(Indice.class);
            //Si la lista que me devuelve es vacia.
            if (!listaIndices.isEmpty()) {
                Indice i = (Indice) listaIndices.get(listaIndices.size() - 1);
                idIndice = i.getIdIndice() + 1;
            }
            indice.setIdIndice(idIndice);
            indice.setNombre(nodo.toString());
            indice.setIndiceidIndice(padre);
            contP.crearEntidad(indice);
        }
    }

    /**
     * Método que transforma un TreeNode en objetos de tipo Indice y los 
     * guarda en la base de datos.
     * @param menu TreeNode correspondiente a un menu editable
     * @throws PreexistingEntityException
     * @throws Exception 
     */
    public void guardarIndice(TreeNode menu) throws PreexistingEntityException, Exception {
        if (menu.getChildCount() == 1) {
            TreeNode principal = menu.getChildren().get(0);
            //Borraré todos los elementos que tenia en la base de datos
            borrarIndice();
            //Crearé el nuevo indice
            crearNodo(principal);
            //Asociaré las url a los elementos del indice
            asociarUrl();
        }
    }

    /**
     * Método recursivo que crea los indices en la base de datos.
     * @param nodo Indice que crearemos
     * @throws PreexistingEntityException
     * @throws Exception t
     */
    public void crearNodo(TreeNode nodo) throws PreexistingEntityException, Exception {
        if (nodo.toString().compareTo("Principal") == 0) {
            crearIndice(nodo, null);
        } else {
            Indice padre = buscarIndice(nodo.getParent().toString());
            crearIndice(nodo, padre);
        }
        if (nodo.getChildCount() >= 0) {
            for (int i = 0; i <= nodo.getChildCount() - 1; i++) {
                crearNodo(nodo.getChildren().get(i));
            }
        }
    }

    /**
     * Método que asocia una URL a los indices creados.
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException
     * @throws Exception 
     */
    public void asociarUrl() throws IllegalOrphanException, NonexistentEntityException, Exception {
        //recorro el hasmap
        Iterator it = hsmPaginas.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry e = (Map.Entry) it.next();
            Indice i = buscarIndice(((Indice) e.getKey()).getNombre());
            if (i != null) {
                String url = (String) e.getValue();
                i.setUrl(url);
                contP.editarEntidad(i);
            }
        }
    }

    /**
     * Método que carga el HashMap de las paginas con los indices que tiene
     * url asignada.
     */
    public void cargarHashMap() {
        hsmPaginas = new HashMap<Indice, String>();
        List<Indice> indices = (List<Indice>) contP.obtenerEntidades(Indice.class);
        for (Indice i : indices) {
            if (i.getUrl() != null && i.getUrl().compareTo(i.getNombre() + ".html") == 0) {
                hsmPaginas.put(i, i.getUrl());
            }
        }
    }

    /**
     * Método que añade una url a un elemento de indice.
     * @param indice Indice al que asociaremos la url
     * @param url Url que asociamos al indice
     */
    public void annadirUrl(Indice indice, String url) {
        if (url.compareTo("") != 0) {
            //recorro el hasmap
            Iterator it = hsmPaginas.entrySet().iterator();
            boolean repetido = false;
            while (it.hasNext()) {
                Map.Entry e = (Map.Entry) it.next();
                //miro si ya existe ese indice en el hashMap
                if (((Indice) e.getKey()).getNombre().compareTo(indice.getNombre()) == 0 && repetido == false) {
                    e.setValue(url);
                    repetido = true;
                }
            }
            if (!repetido) {
                //Si llego aqui es que no estaba en el hashmap y por tanto lo añado al final
                hsmPaginas.put(indice, url);
            }
        }
    }

    /**
     * Método que busca un indice por su nombre en la base de datos.
     * @param nombreIndice Nombre del índice
     * @return indice
     */
    public Indice buscarIndice(String nombreIndice) {
        List<? extends Entidad> indices = contP.obtenerEntidades(Indice.class);
        for (Entidad i : indices) {
            if (((Indice) i).getNombre().compareTo(nombreIndice) == 0) {
                return (Indice) i;
            }
        }
        return null;
    }

    /**
     * Método que borra todos los indices de la base de datos.
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException 
     */
    public void borrarIndice() throws IllegalOrphanException, NonexistentEntityException {
        List<Indice> indices = (List<Indice>) contP.obtenerEntidades(Indice.class);
        if (!indices.isEmpty()) {
            eliminarIndice(indices.get(0));
        }
    }

    /**
     * Método recursivo que elimina un indice, eliminando primero sus hijos.
     * @param indice Indice a eliminar
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException 
     */
    public void eliminarIndice(Indice indice) throws IllegalOrphanException, NonexistentEntityException {
        //Si el índice tiene hijos borramos los hijos
        if (!indice.getIndiceList().isEmpty()) {
            for (int i = indice.getIndiceList().size() - 1; i >= 0; i--) {
                eliminarIndice(indice.getIndiceList().get(i));
            }
        }
        contP.destroyEntidad(Indice.class, indice.getIdIndice());
    }

    /**
     * Método que devuelve una lista con los elementos que deberian 
     * tener una url.
     * @return lista
     */
    public List<String> comprobarIndice() {
        List<Indice> indices = (List<Indice>) contP.obtenerEntidades(Indice.class);
        List<String> lista = new ArrayList<String>();
        for (Indice i : indices) {
            //Si no tiene hijos
            if (i.getIndiceList().isEmpty()) {
                if (i.getUrl() == null) {
                    lista.add(i.getNombre());
                } else {
                  String url = "webapps\\AplicacionONGD\\paginas\\"+i.getNombre()+".html";  
                    File fichero = new File(url);
                    if (!fichero.exists()) {
                        lista.add(i.getNombre());
                    }
                }
            }
        }
        return lista;
    }
    
    /**
     * Método que comprueba que los hijos de principal tengan hijos.
     * @return true si la comprobación ha sido correcta
     */
    public boolean comprobarDatos(){
        Indice principal = buscarIndice("Principal");
        for(Indice i: principal.getIndiceList()){
            if(i.getIndiceList().isEmpty()){
                return false;
            }
        }
        return true;
    }

    /**
     * Método que devuelve una lista con todos los indices que tiene
     * la base de datos.
     * @return lista de indices
     */
    public List<Indice> obtenerIndice() {
        return (List<Indice>) contP.obtenerEntidades(Indice.class);
    }

    /**
     * Método que devuelve un objeto del tipo ControladorPersistencia.
     * @return contP
     * @see #setContP
     */
    public ControladorPersistencia getContP() {
        return contP;
    }

    /**
     * Método que establece un objeto del tipo ControladorPersistencia.
     * @param contP Nuevo valor de la variable
     * @see #getContP
     */
    public void setContP(ControladorPersistencia contP) {
        this.contP = contP;
    }

    /**
     * Método que devuelve un objeto del tipo ControladorSesion.
     * @return contSesion
     * @see #setContSesion
     */
    public ControladorSesion getContSesion() {
        return contSesion;
    }

    /**
     * Método que establece un objeto del tipo ControladorSesion.
     * @param contSesion Nuevo valor de la variable
     * @see #getContSesion
     */
    public void setContSesion(ControladorSesion contSesion) {
        this.contSesion = contSesion;
    }

    /**
     * Método que devuelve la variable hsmPaginas.
     * @return hsmPaginas
     * @see #setHsmPaginas
     */
    public HashMap<Indice, String> getHsmPaginas() {
        return hsmPaginas;
    }

    /**
     * Método que establece la variable hsmPaginas.
     * @param hsmPaginas Nuevo valor de la variable
     * @see #getHsmPaginas
     */
    public void setHsmPaginas(HashMap<Indice, String> hsmPaginas) {
        this.hsmPaginas = hsmPaginas;
    }
}